//
// opcodes.h
// the Potion VM instruction set (heavily based on Lua's)
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#ifndef POTION_OPCODES_H
#define POTION_OPCODES_H

#define OP_CODE_BITS 6
#define OP_A_BITS   10
#define OP_B_BITS   16

#define OP_CODE_MAX ((1 << OP_CODE_BITS) - 1)
#define OP_A_MAX    ((1 << OP_A_BITS) - 1)
#define OP_B_MAX    ((1 << OP_B_BITS) - 1)

#pragma pack(push, 1)
// TODO hsq .code 用 6 bit 就够了；减小 .a 增大 .b ；
//      如 OP_LOADPN.b 是否溢出； OP_JMP 用 .a 和 .b 表示跳转偏移？。
//      如不够，用后一个 PN_OP 来表示数据？
typedef struct {
  u8 code:OP_CODE_BITS;
  int a:  OP_A_BITS;
  int b:  OP_B_BITS;
} PN_OP;

#pragma pack(pop)

// 取索引 n 处的 PN_OP （结构值，而非结构指针）。
#define PN_OP_AT(proto, n) ((PN_OP *)((PNAsm *)(proto)->ops)->ptr)[n]
// 取 PN_OP 数量。
#define PN_OP_LEN(proto)   (PN_FLEX_SIZE((proto)->ops) / sizeof(PN_OP))

// NOTE: 未使用 OP_NONE OP_GETTABLE OP_TAILCALL
// TODO hsq 尾调用优化？
enum PN_OPCODE {
  OP_NONE,      // 未使用
  OP_MOVE,      // regDst=, regSrc
  OP_LOADVAL,   // regDst=, .valuesIdx: 加载一个值
  OP_LOADPN,    // regDst=, $PN: 加载一个 PN
  OP_SELF,      // regDst=: 加载 self
  OP_NEWTUPLE,  // regDst=: 创建 0 项的空元组
  OP_SETTUPLE,  // regTup=, regItem: 向元组中压入一项
  OP_GETLOCAL,  // regDst=, regLocalsSrc: 加载局部变量，按需解除弱引用
  OP_SETLOCAL,  // regSrc, regLocalsDst=: 设置局部变量，按需解除弱引用
  OP_GETUPVAL,  // regDst=, regUpvalsSrc: 加载弱引用中的 upval 变量
  OP_SETUPVAL,  // regSrc, regUpvalsDst=: 设置弱引用中的 upval 变量
  OP_GETTABLE,  // 未使用
  OP_SETTABLE,  // regTbl, regVal: 设置表项， &regKey==&regTbl+1
  OP_NEWLICK,   // regBegin=, regEnd: 构建 PNLick 存入 regBegin
  OP_GETPATH,   // regObj=, regProp: 取对象的属性值
  OP_SETPATH,   // regObj, regVal: 设置对象的属性值， &regProp==&regObj+1
  OP_ADD,       // regV1=, regV2: 整数+ ｜ 消息+
  OP_SUB,       // regV1=, regV2: 整数- ｜ 消息-
  OP_MULT,      // regV1=, regV2: 整数* ｜ 消息*
  OP_DIV,       // regV1=, regV2: 整数/ ｜ 消息/
  OP_REM,       // regV1=, regV2: 整数% ｜ 消息%
  OP_POW,       // regV1=, regV2: 整数 pow() ， **
  OP_NOT,       // regV=: 取反得到 bool 值
  OP_CMP,       // regV1=, regV2: v1 - v2， <=> ，现在实现同 PHP7 中 a<=>b: a>b->1, a==b->0,a<b->-1
  OP_EQ,        // regV1=, regV2: ==
  OP_NEQ,       // regV1=, regV2: !=
  OP_LT,        // regV1=, regV2: 整数<
  OP_LTE,       // regV1=, regV2: 整数<=
  OP_GT,        // regV1=, regV2: 整数>
  OP_GTE,       // regV1=, regV2: 整数>=
  OP_BITN,      // regV=: 整数～ ｜ 消息～
  OP_BITL,      // regV1=, regV2: 整数<< ｜ 消息<<
  OP_BITR,      // regV2=, regV2: 整数>> ｜ 消息>>
  OP_PIPE,      // regV2=, regV2: 整数| ｜ 消息|
  OP_CARET,     // regV2=, regV2: 整数^ ｜ 消息^
  OP_AMP,       // regV2=, regV2: 整数& ｜ 消息&
  OP_DEF,       // regVT=, regMtd: 注册消息响应闭包，&regCl==&regVT ， &regMsg==&regVT+1
  OP_BIND,      // regMsg=, regRcv: bind(rcv, msg), 在 rcv 的虚表中查找绑定到 msg 的对象， &regObj==&regMsg
  OP_MESSAGE,   // 同 OP_BIND ，但如结果是闭包且其参数签名为 nil 则调用并存储该返回值
  OP_JMP,       // _, ipOff: 指令索引 += ipOff
  OP_TEST,      // regV=: 得到 bool 值
  OP_TESTJMP,   // regCond, ipOff: 为真则 指令索引 += ipOff
  OP_NOTJMP,    // regCond, ipOff: 为假则 指令索引 += ipOff
  OP_NAMED,     // regCl, regArg: 在闭包签名元组中查找参数名的索引，找到则在栈中定位并存储参数值；
                //    栈布局见 PN_ARG_TABLE ；&regSig==&regArg-1 。
  OP_CALL,      // regCl=, regEnd: cl 后续为 self 和参数序列；调用 potion_vm 或跳转到它；
                // regVT=, regEnd: 用 vt 创建 PNObject 作为 self ，取其 ctor 为 cl ，余同上；
                // regObj=, regEnd: 取 obj.call 为 cl ， obj 为 self ，调用 potion_call(P, cl, obj, ...)
                //    若 obj.call 非 cl ，则其作为结果。
  OP_CALLSET,   // regDst=, regObj: 取 regObj 处对象的 .callset 闭包 ｜ VM.callset
  OP_TAILCALL,  // 未使用
  OP_RETURN,    // regVal: 对于 potion_vm 内返回，返回值写入 OP_CALL 的 regCl
  OP_PROTO,     // regDst=, .protosIdx: 用原型构造闭包，并用后续指令填充闭包的 upval
  OP_CLASS      // regDst=, regCtor: 定义类； regDst 是父类，无构造函数则 &regCtor==&regDst
};

#endif
